/**
 * 
 */
package com.nwpusp.model;

import java.util.ArrayList;

import com.nwpusp.model.barrier.Barrier;
import com.nwpusp.model.barrier.BarrierManager;
import com.nwpusp.model.characters.CharacterManager;
import com.nwpusp.model.characters.GameCharacter;
import com.nwpusp.model.location.Acceleration;
import com.nwpusp.model.location.Axis;
import com.nwpusp.model.location.Size;
import com.nwpusp.model.players.Player;
import com.nwpusp.model.props.Prop;
import com.nwpusp.model.props.PropManager;

/**
 * @author JustinCao
 *
 */
public class Scenario {
	private BarrierManager bm = null;
	private CharacterManager cm = null;
	private PropManager pm = null;
	private EntityContainer globalContainer = null;
	private Size size = null;
	private HighlightScope highlight = null;
	private Acceleration gravity = new Acceleration(0, 0, -4);
	private TrackOuterArea trackOuterArea = null;
	private TrackManager trackMgr = null;
	private ArrayList<Axis> rawStaticEntityAxes = null;
	
	public Scenario(int xSize, int ySize, int zSize) {
		size = new Size(xSize, ySize, zSize);
		highlight = new HighlightScope(0, 0, ((ySize * 16) / (9) ), (ySize));
		trackOuterArea = new TrackOuterArea((Size)size.clone());
		GameCharacter.setTrackOuterArea(trackOuterArea);
		trackMgr = new TrackManager(6, ySize, xSize);
		Init();
	}
	
	private class HighlightScope extends Entity {
		public HighlightScope(int xPos, int yPos, int xSize, int ySize) {
			setPosAndSize(xPos, yPos, xSize, ySize);
		}
		
		public void setPosAndSize(int xPos, int yPos, int xSize, int ySize) {
			setPosition(new Axis(xPos, yPos, 0));
			setSize(new Size(xSize, ySize, Scenario.this.size.getZ()));
		}
		
		public void Move(int dx, int dy) {
			setPosition(new Axis(getPosition().getX() + dx, getPosition().getY() + dy, getPosition().getZ()));
		}
	}
	
	public void Init() {
		rawStaticEntityAxes = null;
		if(globalContainer == null) {
			globalContainer = new EntityContainer();
			bm = new BarrierManager(globalContainer, trackMgr);
			cm = new CharacterManager(globalContainer, trackMgr);
			pm = new PropManager(globalContainer, trackMgr);
		}
		else {
			globalContainer.clear();
			bm.clear();
			cm.clear();
			pm.clear();
		}
		bm.Init();
		pm.Init();
	}
	
	public void addCharacter(GameCharacter c) {
		if(cm != null) {
			cm.add(c);
		}
	}
	
	public void removeCharacter(GameCharacter c) {
		if(cm != null) {
			cm.remove(c);
		}
	}

	synchronized public boolean Tick(GameCharacter local) {
		if(cm == null) {
			return false;
		}
		boolean retTrue = false;
		for(Entity e : cm) {
			GameCharacter c = (GameCharacter)e;
			if(c.getStatus() != GameCharacter.CHARACTERSTS_ReachEnd) {
				boolean reachEnd = c.tickMovement(gravity);
				if(reachEnd) {
					c.reachEnd(this.size);
					if(local == c) {
						retTrue = true;
					}
				}
			}
		}
		if(local.getStatus() != GameCharacter.CHARACTERSTS_ReachEnd) {
			int oldX = highlight.getPosition().getX();
			int oldY = highlight.getPosition().getY();
			Size hlSize = highlight.getSize();
			int newX = local.getPosition().getX() - hlSize.getX() / 2;
			int newY = local.getPosition().getY() - hlSize.getY() / 2;
			if(newX < 0)
				newX = 0;
			if(newX > (this.size.getX() - hlSize.getX()))
				newX = this.size.getX() - hlSize.getX();
			if(newY < 0)
				newY = 0;
			if(newY > (this.size.getY() - hlSize.getY()))
				newY = this.size.getY() - hlSize.getY();
			highlight.Move(newX - oldX, newY - oldY);
			return false;
		}
		return true;
	}

	synchronized public ArrayList<Entity> getDispEntities(Player local) {
		ArrayList<Entity> ents = new ArrayList<Entity>();
		GameCharacter lc = local.getCh();
		int idx = globalContainer.indexOf(lc);
		for(int i = idx + 1; i < globalContainer.size(); i++) {
			Entity e = globalContainer.elementAt(i);
			if(e.isCollide(highlight)) {
				Entity dispEnt = (Entity)e.clone();
				int newX = dispEnt.getPosition().getX() - highlight.getPosition().getX();
				int newY = dispEnt.getPosition().getY() - highlight.getPosition().getY();
				int newZ = dispEnt.getPosition().getZ() - highlight.getPosition().getZ();
				dispEnt.setPosition(new Axis(newX, newY, newZ));
				ents.add(dispEnt);
			}
			else {
				break;
			}
		}
		
		for(int i = idx; i >= 0; i--) {
			Entity e = globalContainer.elementAt(i);
			if(e.isCollide(highlight)) {
				Entity dispEnt = (Entity)e.clone();
				int newX = dispEnt.getPosition().getX() - highlight.getPosition().getX();
				int newY = dispEnt.getPosition().getY() - highlight.getPosition().getY();
				int newZ = dispEnt.getPosition().getZ() - highlight.getPosition().getZ();
				dispEnt.setPosition(new Axis(newX, newY, newZ));
				ents.add(dispEnt);
			}
			else {
				break;
			}
		}
		globalContainer.Sort();
		return ents;
	}
	
	public boolean isExist(Prop p) {
		return (this.pm.getPropBySeid(p.getStaticEntityId()) != null);
	}
	
	public boolean isExist(Barrier b) {
		return (this.bm.getBarrierBySeid(b.getStaticEntityId()) != null);
	}

	public void removeStaticEntity(StaticEntity se) {
		if(se instanceof Prop) {
			this.pm.remove(se);
		}
		else if(se instanceof Barrier) {
			this.bm.remove(se);
		}
	}
	
	public void removeProp(int seid) {
		this.pm.removeProp(seid);
	}
	
	public void removeBarrier(int seid) {
		this.bm.removeBarrier(seid);
	}

	public ArrayList<StaticEntity> getCollideEntities(GameCharacter ch) {
		ArrayList<StaticEntity> ses = new ArrayList<StaticEntity>();
		for(Entity be : bm) {
			if(be.isCollide(ch)) {
				ses.add((StaticEntity)be);
			}
		}
		for(Entity pe : pm) {
			if(pe.isCollide(ch)) {
				ses.add((StaticEntity)pe);
			}
		}
		return ses;
	}
	
	public void arrangeAllCharacters() {
		if(cm != null) {
			cm.arrangeAllCharacters();
		}
	}
	
	// Client only
	public void ArrangeAllStaticEntitesByServer(ArrayList<Axis> axes) {
		this.rawStaticEntityAxes = axes;
		for(Axis pos : rawStaticEntityAxes) {
			pm.allocProp(pos);
			bm.allocBarrier(pos);
		}
	}

	public void ArrangeAllStaticEntities() {
		getRawStaticEntityAxes();
		for(Axis pos : rawStaticEntityAxes) {
			pm.allocProp(pos);
			bm.allocBarrier(pos);
		}
	}
	
	public ArrayList<Axis> getRawStaticEntityAxes() {
		if(rawStaticEntityAxes == null) {
			rawStaticEntityAxes = this.trackMgr.getAllStaticEntityRawAxes();
		}
		return rawStaticEntityAxes;
	}

	public Size getSize() {
		return size;
	}
}
